use codegraph_core::{
    RelationshipDetectionEngine, RelationshipEvidence, EvidenceType, ConfidenceScore,
    CodeNode, CodeEdge, EdgeType, NodeType, NodeId, EdgeId, SourceLocation,
    NodeMetadata, EdgeMetadata, Result,
};
use codegraph_core::relationship_detection::RelationshipConfig;
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;

// Mock implementations for testing
struct MockNode {
    id: Uuid,
    node_type: NodeType,
    name: String,
    location: SourceLocation,
    metadata: NodeMetadata,
    edges: Vec<MockEdge>,
}

impl CodeNode for MockNode {
    fn id(&self) -> NodeId {
        self.id
    }

    fn node_type(&self) -> &NodeType {
        &self.node_type
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn location(&self) -> &SourceLocation {
        &self.location
    }

    fn language(&self) -> &str {
        "python"
    }

    fn metadata(&self) -> &NodeMetadata {
        &self.metadata
    }

    fn edges(&self, _edge_type: Option<&EdgeType>) -> Vec<&dyn CodeEdge> {
        self.edges.iter().map(|e| e as &dyn CodeEdge).collect()
    }

    fn add_edge(&mut self, _edge: Box<dyn CodeEdge>) -> Result<()> {
        Ok(())
    }

    fn remove_edge(&mut self, _id: EdgeId) -> Result<bool> {
        Ok(false)
    }

    fn update_metadata(&mut self, metadata: NodeMetadata) -> Result<()> {
        self.metadata = metadata;
        Ok(())
    }
}

struct MockEdge {
    id: Uuid,
    source: Uuid,
    target: Uuid,
    edge_type: EdgeType,
    metadata: EdgeMetadata,
}

impl CodeEdge for MockEdge {
    fn id(&self) -> EdgeId {
        self.id
    }

    fn source(&self) -> NodeId {
        self.source
    }

    fn target(&self) -> NodeId {
        self.target
    }

    fn edge_type(&self) -> &EdgeType {
        &self.edge_type
    }

    fn metadata(&self) -> &EdgeMetadata {
        &self.metadata
    }

    fn update_metadata(&mut self, metadata: EdgeMetadata) -> Result<()> {
        self.metadata = metadata;
        Ok(())
    }
}

fn create_test_nodes() -> Vec<Arc<MockNode>> {
    vec![
        Arc::new(MockNode {
            id: Uuid::new_v4(),
            node_type: NodeType::Function,
            name: "calculate_total".to_string(),
            location: SourceLocation {
                file_path: "calculator.py".to_string(),
                start_line: 10,
                start_column: 1,
                end_line: 20,
                end_column: 1,
                byte_offset: 200,
                byte_length: 150,
            },
            metadata: NodeMetadata {
                visibility: Some("public".to_string()),
                modifiers: Vec::new(),
                documentation: Some("Calculate total amount".to_string()),
                annotations: Vec::new(),
                complexity: Some(5),
                custom: HashMap::new(),
            },
            edges: vec![],
        }),
        Arc::new(MockNode {
            id: Uuid::new_v4(),
            node_type: NodeType::Function,
            name: "calculate_tax".to_string(),
            location: SourceLocation {
                file_path: "calculator.py".to_string(),
                start_line: 25,
                start_column: 1,
                end_line: 35,
                end_column: 1,
                byte_offset: 400,
                byte_length: 120,
            },
            metadata: NodeMetadata {
                visibility: Some("public".to_string()),
                modifiers: Vec::new(),
                documentation: Some("Calculate tax amount".to_string()),
                annotations: Vec::new(),
                complexity: Some(3),
                custom: HashMap::new(),
            },
            edges: vec![],
        }),
        Arc::new(MockNode {
            id: Uuid::new_v4(),
            node_type: NodeType::Class,
            name: "Calculator".to_string(),
            location: SourceLocation {
                file_path: "calculator.py".to_string(),
                start_line: 1,
                start_column: 1,
                end_line: 50,
                end_column: 1,
                byte_offset: 0,
                byte_length: 800,
            },
            metadata: NodeMetadata {
                visibility: Some("public".to_string()),
                modifiers: Vec::new(),
                documentation: Some("Main calculator class".to_string()),
                annotations: vec!["@dataclass".to_string()],
                complexity: Some(15),
                custom: HashMap::new(),
            },
            edges: vec![],
        }),
    ]
}

#[tokio::test]
async fn test_relationship_detection_engine_creation() {
    let config = RelationshipConfig::default();
    let engine = RelationshipDetectionEngine::new(config);
    
    let stats = engine.get_detection_stats().await.expect("Failed to get stats");
    assert_eq!(stats.total_detected, 0);
    assert_eq!(stats.high_confidence, 0);
    assert_eq!(stats.average_confidence, 0.0);
    
    println!("✅ Relationship detection engine creation test passed");
}

#[tokio::test]
async fn test_relationship_detection_with_semantic_analysis() {
    let mut config = RelationshipConfig::default();
    config.min_confidence = 0.3; // Lower threshold for testing
    config.enable_semantic_analysis = true;
    
    let engine = RelationshipDetectionEngine::new(config);
    let nodes = create_test_nodes();
    
    // Convert to trait objects
    let node_refs: Vec<Arc<dyn CodeNode>> = nodes.into_iter()
        .map(|n| n as Arc<dyn CodeNode>)
        .collect();
    
    let relationships = engine.detect_relationships(&node_refs).await
        .expect("Failed to detect relationships");
    
    println!("Detected {} relationships", relationships.len());
    
    // Check for semantic relationships between similar function names
    let semantic_rels: Vec<_> = relationships.iter()
        .filter(|r| r.evidence.iter().any(|e| e.evidence_type == EvidenceType::Semantic))
        .collect();
    
    if !semantic_rels.is_empty() {
        println!("Found {} semantic relationships", semantic_rels.len());
        for rel in &semantic_rels {
            println!("  Semantic relationship: confidence {:.2}", rel.confidence);
            for evidence in &rel.evidence {
                println!("    Evidence: {}", evidence.description);
            }
        }
    }
    
    println!("✅ Semantic analysis test passed");
}

#[tokio::test]
async fn test_relationship_detection_stats() {
    let config = RelationshipConfig::default();
    let engine = RelationshipDetectionEngine::new(config);
    let nodes = create_test_nodes();
    
    let node_refs: Vec<Arc<dyn CodeNode>> = nodes.into_iter()
        .map(|n| n as Arc<dyn CodeNode>)
        .collect();
    
    let relationships = engine.detect_relationships(&node_refs).await
        .expect("Failed to detect relationships");
    
    let stats = engine.get_detection_stats().await
        .expect("Failed to get detection stats");
    
    println!("Detection Statistics:");
    println!("  Total detected: {}", stats.total_detected);
    println!("  High confidence: {}", stats.high_confidence);
    println!("  Average confidence: {:.2}", stats.average_confidence);
    println!("  By type: {:?}", stats.by_type);
    println!("  By evidence: {:?}", stats.by_evidence);
    
    // Verify stats consistency
    assert_eq!(stats.total_detected, relationships.len());
    
    if stats.total_detected > 0 {
        assert!(stats.average_confidence >= 0.0 && stats.average_confidence <= 1.0);
        assert!(stats.high_confidence <= stats.total_detected);
    }
    
    println!("✅ Detection statistics test passed");
}

#[tokio::test]
async fn test_confidence_scoring() {
    let config = RelationshipConfig::default();
    let _engine = RelationshipDetectionEngine::new(config);
    
    // Test confidence score validation
    let test_scores: Vec<ConfidenceScore> = vec![0.0, 0.25, 0.5, 0.75, 1.0];
    
    for score in test_scores {
        assert!(score >= 0.0 && score <= 1.0, "Invalid confidence score: {}", score);
    }
    
    println!("✅ Confidence scoring test passed");
}

#[tokio::test]
async fn test_evidence_types() {
    // Test all evidence types
    let evidence_types = vec![
        EvidenceType::Syntactic,
        EvidenceType::Semantic,
        EvidenceType::TypeBased,
        EvidenceType::ImportBased,
        EvidenceType::DocumentationBased,
        EvidenceType::MLPrediction,
        EvidenceType::Transitive,
        EvidenceType::CrossLanguage,
    ];
    
    for evidence_type in evidence_types {
        let evidence = RelationshipEvidence {
            evidence_type: evidence_type.clone(),
            description: format!("Test evidence of type {:?}", evidence_type),
            confidence_contribution: 0.8,
            location: None,
            context: HashMap::new(),
        };
        
        assert_eq!(evidence.evidence_type, evidence_type);
        assert!(evidence.confidence_contribution >= 0.0 && evidence.confidence_contribution <= 1.0);
    }
    
    println!("✅ Evidence types test passed");
}

#[tokio::test]
async fn test_relationship_clearing() {
    let config = RelationshipConfig::default();
    let engine = RelationshipDetectionEngine::new(config);
    let nodes = create_test_nodes();
    
    let node_refs: Vec<Arc<dyn CodeNode>> = nodes.into_iter()
        .map(|n| n as Arc<dyn CodeNode>)
        .collect();
    
    // Detect relationships
    let _relationships = engine.detect_relationships(&node_refs).await
        .expect("Failed to detect relationships");
    
    // Clear relationships
    engine.clear_relationships().await
        .expect("Failed to clear relationships");
    
    // Verify relationships are cleared
    let stats = engine.get_detection_stats().await
        .expect("Failed to get stats after clearing");
    
    assert_eq!(stats.total_detected, 0);
    assert_eq!(stats.high_confidence, 0);
    assert_eq!(stats.average_confidence, 0.0);
    
    println!("✅ Relationship clearing test passed");
}
